#include "Framework.h"
#include "Shader.h"
#include <assimp.h>
#include "Camera.h"
#include "DepthRenderTarget.h"
#include "ColorRenderTarget.h"
#include "Game.h"
#include "DisplayCharacter.h"

#define RENDER_WIDTH 800
#define RENDER_HEIGHT 600

#define GL_CHECK(x) {\
(x);\
GLenum error = glGetError();\
if (GL_NO_ERROR != error) {\
printf("%s", gluErrorString(error));\
}\
}

// Note: See the SMFL documentation for info on setting up fullscreen mode
// and using rendering settings
// http://www.sfml-dev.org/tutorials/1.6/window-window.php
sf::WindowSettings settings(24, 8, 2);
//sf::Window window(sf::VideoMode(800, 600), "CS248", sf::Style::Close, settings);
sf::RenderWindow window(sf::VideoMode(800, 600), "CS248", sf::Style::Close, settings);

// This is a clock you can use to control animation.  For more info, see:
// http://www.sfml-dev.org/tutorials/1.6/window-time.php
sf::Clock clck;
float ElapsedTime;

void initOpenGL();
void handleInput();
void renderFrame();
void renderAllScenes();
void setMatrices();
void positionCamera();
void unbindAllTextures();
void cleanupAfterRender();
void setMatricesLight(GLdouble x, GLdouble y, GLdouble z, GLdouble lx, GLdouble ly, GLdouble lz);
void genShadowMapWithDepthRender();
void updateLights();
void renderParticles();
void updateEnemyAnimations();

// save image to debug
sf::Image iimmaaggee;

int prevMouseX = 400;
float accumulator = 0;
bool mouseDown = false;
DepthRenderTarget *depthRenderCamTarget;
DepthRenderTarget *depthRenderLightTarget;

ColorRenderTarget *colorRenderCamTarget;
ColorRenderTarget *colorRenderNormalTarget;
ColorRenderTarget *colorRenderNormalOutlineTarget;
ColorRenderTarget *colorRenderOutlineTarget;
ColorRenderTarget *colorRenderThickOutlineTarget;
ColorRenderTarget *colorRenderTexSpecTarget;

Camera *camera;
Renderer *renderer;
KdTree *kdTree;
Game *game;

//Frame buffer ids
GLuint fboId;

//Light variables
//float light0Position[4] = {-100.0f, 200.5f, 100.0f, 0.0f};
float light0Position[4] = {-2.9f, 4.5f, 2.5f, 0.0f};
float light0Look[3] = {0.0f, 0.0f, 0.0f};

const unsigned int SHADOW_RATIO = 10; //10

int main(int argc, char** argv) {

	//This is directional
	glEnable(GL_LIGHT0);

	initOpenGL();

	srand(334);	

	depthRenderCamTarget = new DepthRenderTarget(window.GetWidth(), window.GetHeight(), 1);
	depthRenderLightTarget = new DepthRenderTarget(window.GetWidth(),window.GetHeight(), SHADOW_RATIO);

	colorRenderCamTarget = new ColorRenderTarget(window.GetWidth(),window.GetHeight());
        colorRenderNormalTarget = new ColorRenderTarget(window.GetWidth(),window.GetHeight());
        colorRenderNormalOutlineTarget = new ColorRenderTarget(window.GetWidth(), window.GetHeight());
        colorRenderOutlineTarget = new ColorRenderTarget(window.GetWidth(),window.GetHeight()); 
        colorRenderThickOutlineTarget = new ColorRenderTarget(window.GetWidth(), window.GetHeight());
	colorRenderTexSpecTarget = new ColorRenderTarget(window.GetWidth(), window.GetHeight());

	game = new Game();
	camera = new Camera();
	kdTree = new KdTree();
	AIEngine *aiEngine = new AIEngine(camera, kdTree);
	renderer = new Renderer(camera, aiEngine);
	kdTree->genAllTriangles(renderer->getScene(0), renderer->getScene(0)->mRootNode);
	kdTree->build_tree();
	game->setKdTree(kdTree);
	game->setRenderer(renderer);
	game->setAIEngine(aiEngine);

	while (window.IsOpened()) {
	        ElapsedTime = clck.GetElapsedTime();
		updateEnemyAnimations();
		clck.Reset();
        	handleInput();
		game->getAIEngine()->updateEnemies();
        	renderFrame();
		game->getAIEngine()->characterCollidesWithEnemy();
        	window.Display();
    	}

	return 0;
}



void initOpenGL() {
#ifdef FRAMEWORK_USE_GLEW
	GLint error = glewInit();
	if (GLEW_OK != error) {
		std::cerr << glewGetErrorString(error) << std::endl;
		exit(-1);
	}
	if (!GLEW_VERSION_2_0 || !GL_EXT_framebuffer_object) {
		std::cerr << "This program requires OpenGL 2.0 and FBOs" << std::endl;
		exit(-1);
	}
#endif

	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glViewport(0,0,window.GetWidth(), window.GetHeight());
	glClearColor(0.69f, 0.88f, 1.0f, 1.0f);

	glEnable(GL_CULL_FACE);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	window.PreserveOpenGLStates(true); 
}



void handleInput() {
    sf::Event evt;
    aiVector3D oldCamPos(camera->x, 0.3, camera->z);
    bool positionChanged = false;

    while (window.GetEvent(evt)) {
	switch (evt.Type) {
        case sf::Event::Closed: 
		window.Close(); 
		break;
        case sf::Event::Resized: 
		glViewport(0, 0, evt.Size.Width, evt.Size.Height);
		break;
	case sf::Event::KeyPressed:
		if (evt.Key.Code == sf::Key::W) {
			camera->idle = false;
			positionChanged = true;
			camera->move(1.5);
		} else if (evt.Key.Code == sf::Key::S) {
			camera->idle = false;
			positionChanged = true;
			camera->move(-1.5);
		} else if (evt.Key.Code == sf::Key::A) {
			camera->idle = false;
			positionChanged = true;
			camera->strafe(-1.5);
		} else if (evt.Key.Code == sf::Key::D) {
			camera->idle = false;
			positionChanged = true;
			camera->strafe(1.5);
		} else if (evt.Key.Code == sf::Key::Up) {
			light0Position[0] += 0.3;
		} else if (evt.Key.Code == sf::Key::Down) {
			light0Position[0] -= 0.3;
		} else if (evt.Key.Code == sf::Key::Left) {
			light0Position[2] -= 0.3;
		} else if (evt.Key.Code == sf::Key::Right) {
			light0Position[2] += 0.3;
		}
		if (positionChanged) {
    			aiVector3D newCamPos(camera->x, 0.3, camera->z);
			aiVector3D dir = newCamPos - oldCamPos;
		         
                        float delta = 0.1;               
                        if (kdTree->characterCollisionTest(oldCamPos, dir)){
                        	aiVector3D dn = dir.Normalize();
				camera->x = oldCamPos.x - (delta*dn).x;
				camera->z = oldCamPos.z - (delta*dn).z;
			}
                        
		}
		break;
	case sf::Event::KeyReleased:
		camera->idle = true;
		break;
	case sf::Event::MouseButtonPressed:
		mouseDown = true;
		break;
	case sf::Event::MouseButtonReleased:
		mouseDown = false;
		break;
	case sf::Event::MouseMoved:
		if (mouseDown) {
			int currentX = evt.MouseMove.X;
	
			int differenceX = currentX - prevMouseX;

			if (differenceX < 0) {
				camera->rotateXDegrees(2.0);
		
			} else if (differenceX > 0) {
				camera->rotateXDegrees(-2.0);

			}
			prevMouseX = currentX;
		}
		break;
	default: 
		break;
        }
    }
}


void updateEnemyAnimations() {
	accumulator += ElapsedTime;

	if (accumulator >= 1.00) {
		accumulator = 1.00;		
	}

	float timeStep = 1.00;
	while (accumulator >= timeStep) {
		for (unsigned int i = 0; i < game->getAIEngine()->getNumEnemies(); i++) {
			game->getAIEngine()->getEnemy(i)->toggleAnim();
		}
		//accumulator -= timeStep;
		accumulator = 0;
		break;
	}
}

void debugDepthTexture(GLuint textureID_){

    glUseProgram(0);

    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);


    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
//  glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
    gluOrtho2D(-1, 1, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnable( GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureID_); //depthTextureID_

   glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0, -1.0, 0);    // Bottom Left Of The Texture and Quad
        glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0, -1.0, 0);    // Bottom Right Of The Texture and Quad
        glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0, 1.0, 0);    // Top Right Of The Texture and Quad
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0, 1.0, 0);

    glEnd();


}


void genAmbientSceneWithDepthRender()
{ 
  renderer->setCurrentShader(renderer->getShadowShader());
  glUseProgram(renderer->getCurrentShader()->programID()); 
 
  depthRenderCamTarget->bind();
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
   
  // this is to position the camera; can see a little bit of the depth
  // texture. The color is very light though.

  setMatrices(); // positionCamera is contained in setMatrices
 
  // draw
  renderer->renderAllScenes(); 
    
  renderer->disableVertexAttributeArrays();
  depthRenderCamTarget->unbind();
  
  // up to now the texture_ID should have smthing
  //debugDepthTexture(depthRenderCamTarget->depthTextureID());
   
}
/**
  *   STEP 2: RENDER ONLY the COLOR OF the AMBIENT IMAGE TO A TEXTURE!!!
  *   HAS TO DO WITH colorRenderCamTarget
  *   GET THE TEXTURE WITH colorRenderCamTarget->colorTextureID()
 **/
void genAmbientSceneWithColorRender()
{

  renderer->setCurrentShader(renderer->getPhongShader());
  glUseProgram(renderer->getCurrentShader()->programID()); 

  colorRenderCamTarget->bind();

  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
   
  setMatrices(); // positionCamera is contained in setMatrices
 
  // draw
  renderer->renderAllScenes(); 
    
  renderer->disableVertexAttributeArrays();
  colorRenderCamTarget->unbind();
  
  // up to now the texture_ID should have smthing
  //debugDepthTexture(colorRenderCamTarget->colorTextureID());
   
}
/**
  *   STEP 3: RENDER NORMAL COLOR TO A TEXTURE!!!
  *   HAS TO DO WITH colorRenderNormalTarget
  *   GET THE TEXTURE WITH colorRenderNormalTarget->colorTextureID()
 **/
void genNormalColorSceneWithColorRender()

{
  renderer->setCurrentShader(renderer->getNormalColShader());
  glUseProgram(renderer->getCurrentShader()->programID());
 
  colorRenderNormalTarget->bind();
 
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
   
  setMatrices(); // positionCamera is contained in setMatrices
 
  // draw
  renderer->renderAllScenes(); 
    
  renderer->disableVertexAttributeArrays();
  colorRenderNormalTarget->unbind();

  //debugDepthTexture(colorRenderNormalTarget->colorTextureID());
  //debugDepthTextureWithShader(colorRenderNormalTarget->colorTextureID());

}

/**
  * STEP 4
  * IS ASSOCIATED WITH colorRenderTexSpecTarget
  * GET THE TEXTURE WITH  colorRenderTexSpecTarget->colorTextureID()
 */

void genTexPlusSpecScene()
{
  // Turn the light back on
	GLfloat lightAmbientOn[] = {0.05, 0.05, 0.05, 1.0};
	GLfloat lightDiffuseOn[] = {0.8, 0.8, 0.8, 1.0};
	GLfloat lightSpecularOn[] = {1.0, 1.0, 1.0, 1.0};
	glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbientOn);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuseOn);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecularOn);
	// use PhongToon
	renderer->setCurrentShader(renderer->getPhongToonShader());
        glUseProgram(renderer->getCurrentShader()->programID());


	// Enable Texture7 for shadow map
	//setLightTextureMatrix();
	glActiveTexture(GL_TEXTURE7);
	glBindTexture(GL_TEXTURE_2D, depthRenderLightTarget->depthTextureID());
	renderer->setDrawShadows(true);

	GLint shadow = glGetUniformLocation(renderer->getCurrentShader()->programID(), "shadowMap");
	glUniform1i(shadow, 7);
	glActiveTexture(GL_TEXTURE7);
	glBindTexture(GL_TEXTURE_2D,depthRenderLightTarget->depthTextureID());

	// bind
	colorRenderTexSpecTarget->bind();

        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
       
	setMatrices();
	// draw
	renderer->renderAllScenes(); 
    
	renderer->disableVertexAttributeArrays();
        renderParticles();
	//Render text
	glUseProgram(0);
	window.Draw(*renderer->getStatusStr());
	// unbind
	colorRenderTexSpecTarget->unbind();

        // render to a quad
	//debugDepthTexture(colorRenderTexSpecTarget->colorTextureID());
	renderer->setDrawShadows(false);
	glActiveTexture(GL_TEXTURE7);
	glBindTexture(GL_TEXTURE_2D, 0);
}


/**
  *   STEP 5: RENDER DEPTH MAP TO A TEXTURE !!! 
  *   IS A ONE-TIME THING? OR NOT?
  *   HAS TO DO WITH depthRenderLightTarget
  *   GET THE TEXTURE WITH depthRenderLightTarget->depthTextureID()
 **/
void genShadowMapWithDepthRender()
{
  renderer->setCurrentShader(renderer->getShadowShader());
  glUseProgram(renderer->getCurrentShader()->programID()); 
 
  depthRenderLightTarget->bind();
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
   
  /*
  // Sammy's light matrix
   // can see depth texture if camera is set at the light position; 
  GL_CHECK(glMatrixMode(GL_PROJECTION));
  GL_CHECK(glLoadIdentity());
  GL_CHECK(glOrtho(-100.2, 100.2, -100.2, 100.2, -100.2, 100.2));
  GL_CHECK(glMatrixMode(GL_MODELVIEW));
  GL_CHECK(glLoadIdentity());
  GL_CHECK(gluLookAt(0.0f, 0.0f, 0.0f, 10, -10, 0, 0.0f, 1.0f, 0.0f));//??
  */

  // Yiannis' light matrix
  setMatricesLight(light0Position[0], light0Position[1], light0Position[2], light0Look[0], light0Look[1], light0Look[2]);
  glCullFace(GL_FRONT);
  
  renderer->setLightTextureMatrix();

  // draw
  renderer->renderAllScenes(); 
     
  renderer->disableVertexAttributeArrays();
  depthRenderLightTarget->unbind();
  glCullFace(GL_BACK);

  // up to now the texture_ID should have smthing
  //debugDepthTexture(depthRenderLightTarget->depthTextureID());

  // USE THE FOLLOWING CODE TO save the texture AS A image (DEBUG USE) =)
  //const sf::IntRect SourceRect = sf::IntRect(0,0,0,0); //IntRect(0, 0, 0, 0);
  //iimmaaggee.CopyScreen(window, SourceRect);	 
  //iimmaaggee.SaveToFile("iimmaaggee.jpg");

}

/**
  * DRAW OUTLINE PHASE I: USING NORMAL COL. RESULT
  * STORE THE OUTLINE IN THE TEXTURE ASSOCIATED WITH  colorRenderNormalOutlineTarget
  * GET the TEXTURE with colorRenderNormalOutlineTarget->colorTextureID()
  */
void genNormalOutlines()
{
    	renderer->setCurrentShader(renderer->getOutlineShader());
  	glUseProgram(renderer->getCurrentShader()->programID()); 
        // bind 
        colorRenderNormalOutlineTarget->bind();
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        // set the matrices
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-1, 1, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        // let's use the normal to see outline first
        glEnable(GL_TEXTURE_2D);
      
        // pass in the kernel
        GLint kernel = glGetUniformLocation(renderer->getCurrentShader()->programID(), "kernel");
        int kernelArr [9]= {1, 2, 1, 2, 0, 2, 1, 2, 1};
        glUniform1iv(kernel, 9, kernelArr);

        // pass in the offset
        GLint offset =  glGetUniformLocation(renderer->getCurrentShader()->programID(),"offset");
        float uw = 1.0/RENDER_WIDTH;
        float uh = 1.0/RENDER_HEIGHT;

        float offsetArr [18] = {-uw, -uh, 0.0, -uh, uw, -uh, 
                                -uw, 0.0, 0.0, 0.0, uw, 0.0,
                                -uw, uh, 0.0, uh, uw, uh};
        glUniform2fv(offset, 9, offsetArr);

        // pass in the texture
	GLint texture = glGetUniformLocation(renderer->getCurrentShader()->programID(),"textureMap");
        // BE SURE THAT YOU ACTIVATE SLOT 0 THE WAY THE SHADER IS WRITTEN
        GL_CHECK(glUniform1i(texture, 0));
        GL_CHECK(glActiveTexture(GL_TEXTURE0));
        GL_CHECK(glBindTexture(GL_TEXTURE_2D,colorRenderNormalTarget->colorTextureID()));

	glBegin(GL_QUADS);
	    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0, -1.0, 0);    // Bottom Left Of The Texture and Quad
            glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0, -1.0, 0);    // Bottom Right Of The Texture and Quad
            glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0, 1.0, 0);    // Top Right Of The Texture and Quad
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0, 1.0, 0);
        glEnd();

        colorRenderNormalOutlineTarget->unbind();

	//debugDepthTexture(colorRenderNormalOutlineTarget->colorTextureID());

}

void genThickOutlines()
{
    	renderer->setCurrentShader(renderer->getThicknessShader());
  	glUseProgram(renderer->getCurrentShader()->programID()); 
        // bind 
        colorRenderThickOutlineTarget->bind();
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        // set the matrices
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-1, 1, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        // let's use the normal to see outline first
        glEnable(GL_TEXTURE_2D);
      
        // pass in the kernel
        GLint kernel = glGetUniformLocation(renderer->getCurrentShader()->programID(), "kernel");
        int kernelArr [9]= {1, 1, 1, 1, 1, 1, 1, 1, 1};
        glUniform1iv(kernel, 9, kernelArr);

        // pass in the offset
        GLint offset =  glGetUniformLocation(renderer->getCurrentShader()->programID(),"offset");
        float uw = 1.0/RENDER_WIDTH;
        float uh = 1.0/RENDER_HEIGHT;

        float offsetArr [18] = {-uw, -uh, 0.0, -uh, uw, -uh, 
                                -uw, 0.0, 0.0, 0.0, uw, 0.0,
                                -uw, uh, 0.0, uh, uw, uh};
        glUniform2fv(offset, 9, offsetArr);

        // pass in the texture
	GLint texture = glGetUniformLocation(renderer->getCurrentShader()->programID(),"textureMap");
        // BE SURE THAT YOU ACTIVATE SLOT 0 THE WAY THE SHADER IS WRITTEN
        GL_CHECK(glUniform1i(texture, 0));
        GL_CHECK(glActiveTexture(GL_TEXTURE0));
        GL_CHECK(glBindTexture(GL_TEXTURE_2D,colorRenderNormalOutlineTarget->colorTextureID()));

	glBegin(GL_QUADS);
	    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0, -1.0, 0);    // Bottom Left Of The Texture and Quad
            glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0, -1.0, 0);    // Bottom Right Of The Texture and Quad
            glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0, 1.0, 0);    // Top Right Of The Texture and Quad
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0, 1.0, 0);
        glEnd();

        colorRenderThickOutlineTarget->unbind();

	//debugDepthTexture(colorRenderThickOutlineTarget->colorTextureID());

}


/**
  * 
  * second pass of outlines : use Normal Col + Ambient z-buffer
  */
void genOutlines()
{
        renderer->setCurrentShader(renderer->getOutlineFilterShader());
        glUseProgram(renderer->getCurrentShader()->programID());

        //bind     
	colorRenderOutlineTarget->bind();

        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

        // set the matrices
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-1, 1, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glEnable(GL_TEXTURE_2D);
      
        // pass in the kernel
        GLint kernel = glGetUniformLocation(renderer->getCurrentShader()->programID(), "kernel");
        int kernelArr [9]= {1, 2, 1, 2, 0, 2, 1, 2, 1};
        glUniform1iv(kernel, 9, kernelArr);

        // pass in the offset
        GLint offset =  glGetUniformLocation(renderer->getCurrentShader()->programID(),"offset");
        float uw = 1.0/RENDER_WIDTH;
        float uh = 1.0/RENDER_HEIGHT;

        float offsetArr [18] = {-uw, -uh, 0.0, -uh, uw, -uh, 
                                -uw, 0.0, 0.0, 0.0, uw, 0.0,
                                -uw, uh, 0.0, uh, uw, uh};
        glUniform2fv(offset, 9, offsetArr);

        // pass in the texture : z-buffer
	GLint texture = glGetUniformLocation(renderer->getCurrentShader()->programID(),"textureMap");
        // BE SURE THAT YOU ACTIVATE SLOT 0 THE WAY THE SHADER IS WRITTEN
        GL_CHECK(glUniform1i(texture, 0));
        GL_CHECK(glActiveTexture(GL_TEXTURE0));
        GL_CHECK(glBindTexture(GL_TEXTURE_2D,depthRenderCamTarget->depthTextureID()));

        // pass in the texture : normalOutline
        GLint normalOutline = glGetUniformLocation(renderer->getCurrentShader()->programID(),"normalOutline");
        GL_CHECK(glUniform1i(normalOutline, 1)); // the texture is using TEXTURE SLOT 1
        GL_CHECK(glActiveTexture(GL_TEXTURE1));
        GL_CHECK(glBindTexture(GL_TEXTURE_2D,colorRenderThickOutlineTarget->colorTextureID()));

	glBegin(GL_QUADS);
	    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0, -1.0, 0);    // Bottom Left Of The Texture and Quad
            glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0, -1.0, 0);    // Bottom Right Of The Texture and Quad
            glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0, 1.0, 0);    // Top Right Of The Texture and Quad
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0, 1.0, 0);
        glEnd();

        colorRenderOutlineTarget->unbind();

}


void genScenePlusOutlines()
{
	renderer->setCurrentShader(renderer->getOutlinePlusSceneShader());
  	glUseProgram(renderer->getCurrentShader()->programID()); 
              
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

	 // set the matrices
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-1, 1, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        // let's use the normal to see outline first
        glEnable(GL_TEXTURE_2D);
      
	// pass in the kernel
        GLint kernel = glGetUniformLocation(renderer->getCurrentShader()->programID(), "kernel");
        int kernelArr [9]= {1, 1, 1, 1, 1, 1, 1, 1, 1};
        glUniform1iv(kernel, 9, kernelArr);

        // pass in the offset
        GLint offset =  glGetUniformLocation(renderer->getCurrentShader()->programID(),"offset");
        float uw = 1.0/RENDER_WIDTH;
        float uh = 1.0/RENDER_HEIGHT;

        float offsetArr [18] = {-uw, -uh, 0.0, -uh, uw, -uh, 
                                -uw, 0.0, 0.0, 0.0, uw, 0.0,
                                -uw, uh, 0.0, uh, uw, uh};
        glUniform2fv(offset, 9, offsetArr);


	// pass in the texture : Texture + Specular
	GLint texture = glGetUniformLocation(renderer->getCurrentShader()->programID(),"textureMap");
        // BE SURE THAT YOU ACTIVATE SLOT 0 THE WAY THE SHADER IS WRITTEN
        GL_CHECK(glUniform1i(texture, 0));
        GL_CHECK(glActiveTexture(GL_TEXTURE0));
        GL_CHECK(glBindTexture(GL_TEXTURE_2D,colorRenderTexSpecTarget->colorTextureID()));
   
        // pass in the texture : normalOutline + z-buffer outline
        GLint Outlines = glGetUniformLocation(renderer->getCurrentShader()->programID(),"Outlines");
        GL_CHECK(glUniform1i(Outlines, 1)); // the texture is using TEXTURE SLOT 1
        GL_CHECK(glActiveTexture(GL_TEXTURE1));
        GL_CHECK(glBindTexture(GL_TEXTURE_2D,colorRenderOutlineTarget->colorTextureID()));

        
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0, -1.0, 0.0);    // Bottom Left Of The Texture and Quad
	glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0, -1.0, 0.0);    // Bottom Right Of The Texture and Quad
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0, 1.0, 0.0);    // Top Right Of The Texture and Quad
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0, 1.0, 0.0);
        glEnd();
        

	glDisable(GL_TEXTURE_2D);
      
}

void renderParticles()
{
        renderer->setCurrentShader(renderer->getParticleShader());
        glUseProgram(renderer->getCurrentShader()->programID()); 
        GLint baseSizeHandle =
	glGetUniformLocation(renderer->getCurrentShader()->programID(),"baseSize");

        const float sparkParticleSize = 2.5;
        glUniform1f(baseSizeHandle, sparkParticleSize);
    
        GLint viewportWidthHandle = glGetUniformLocation(renderer->getCurrentShader()->programID(), "viewportWidth");
        GL_CHECK(glUniform1f(viewportWidthHandle, window.GetWidth()));

        setMatrices();

        glDepthMask(GL_FALSE);
	GL_CHECK(glEnable(GL_POINT_SPRITE_ARB));
 	GL_CHECK(glEnable(GL_VERTEX_PROGRAM_POINT_SIZE));

	GLint texHandle = glGetUniformLocation(renderer->getCurrentShader()->programID(), "particleTex");
	GL_CHECK(glUniform1i(texHandle, 0)); // The texture need to be GL_TEXTURE0
	GL_CHECK(glActiveTexture(GL_TEXTURE0));
	renderer->sparkTexture->Bind();
	GL_CHECK(glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE, 1));

	GL_CHECK(glEnable(GL_BLEND));
 	GL_CHECK(glBlendFunc(GL_SRC_ALPHA, GL_ONE));

        // draw particles
        renderer->renderAllParticles(ElapsedTime);       

	GL_CHECK(glDepthMask(GL_TRUE));
	GL_CHECK(glDisable(GL_POINT_SPRITE_ARB));
	GL_CHECK(glDisable(GL_BLEND));
	GL_CHECK(glEnable(GL_VERTEX_PROGRAM_POINT_SIZE));
              
}

void updateLights() {
	setMatrices();
	glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
}

void renderFrame() {

  
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
	updateLights();
	
	// STEP 0 setup light for ambient scene 
                
	GLfloat lightAmbient[] = {0.25, 0.25, 0.25, 1.0};
	GLfloat lightDiffuse[] = {0.0, 0.0, 0.0, 1.0};
	GLfloat lightSpecular[] = {0.0, 0.0, 0.0, 1.0};
    
        //GLfloat lightDiffuse[] = {0.6, 0.6, 0.6, 1.0}; // need to have some kind of diffuse so that we can see the texture 
	//GLfloat lightSpecular[] = {0.3, 0.3, 0.3, 1.0};

	glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular); 
       
	// STEP 1 ambient scene z buffer from the camera
        genAmbientSceneWithDepthRender(); // cleaned up is contained 
       	// END STEP 1
	

	// STEP 2 ambient scene color buffer
        //genAmbientSceneWithColorRender();
        // weird thing here that it shows the mysterious wall whenever i
        // turned on SPECULAR
       
        // END STEP 2
        
        // STEP 3
        // draw with the normalCol shader and save in the texture 
        genNormalColorSceneWithColorRender();
        // END STEP 3
        
	//vvvv STEP 4 Render Texture + Specular Scene
	genTexPlusSpecScene();
	//^^^^^^ END STEP 4

	 
	// STEP 5 Generate shadow z buffer
	renderer->setDrawShadows(false);
        genShadowMapWithDepthRender();


         
	// GEN OUTLINE(S) PHASES
        // PHASE I
        genNormalOutlines();
 
	genThickOutlines();
        
        // PHASE II: NOT CURRENTLY IN USE
	genOutlines();
       
        // THIS IS WHAT CONFLICTS WITH THE PARTICLES
	genScenePlusOutlines();
       


        /*
        glUseProgram(0);
        setMatrices();
	unsigned itemn = 0;
        kdTree->render_kd_tree(kdTree->getRoot(), itemn);
        */

}

void setMatricesLight(GLdouble x, GLdouble y, GLdouble z, GLdouble lx, GLdouble ly, GLdouble lz) {

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-450.2, 450.2, -450.2, 450.2, -450.2, 450.2);
     
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	gluLookAt(0,0,0, -x, -y, -z, 0, 1.0f, 0);

}

aiMatrix4x4 floatArrCol2aiMatrix4x4Row(float* m)
{
  aiMatrix4x4 ret = aiMatrix4x4(m[0], m[1], m[2], m[3],
                                m[4], m[5], m[6], m[7], 
                                m[8], m[9], m[10], m[11], 
                                m[12],m[13], m[14],m[15]);
    
  ret.Transpose();
  return ret;
}

void setMatrices() {
	GLfloat aspectRatio = (GLfloat)window.GetWidth()/window.GetHeight();
	GLfloat nearClip = 0.1f;
	GLfloat farClip = 1000.0f;
	GLfloat fieldOfView = 45.0f; // Degrees

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(fieldOfView, aspectRatio, nearClip, farClip);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	positionCamera();
}

void unbindAllTextures() {
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_2D, 0);
}

void positionCamera() {
  /*
	gluLookAt(camera->x, camera->y,
		camera->z, camera->x + camera->lx,
		camera->y + camera->ly, camera->z + camera->lz,
		  0, 1, 0);
  */
	gluLookAt(camera->x - camera->lx * camera->pushBack, camera->y - camera->ly * camera->pushBack,
		camera->z - camera->lz * camera->pushBack, camera->x - camera->lx * camera->pushBack + camera->lx,
		camera->y - camera->ly * camera->pushBack + camera->ly, camera->z - camera->lz * camera->pushBack + camera->lz,
  	        0, 1, 0);
  
}


